package netfilter

import (
	"fmt"
	"net/netip"
	"sync"
	"time"
	"unsafe"

	"gitlab.xunlei.cn/xacc/fastlogger"
)

/*
#cgo  LDFLAGS: -lwsock32
#cgo  LDFLAGS: -lws2_32
#cgo  CFLAGS: -I ./bin/include
#cgo  LDFLAGS: -L ./bin/nfapi/Release_c_api/win32 -lnfapi

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <ws2tcpip.h>
#include <sys/stat.h>
#define _C_API
#include "nfapi.h"


// netfilter callbacks implement with Golang.
extern void go_threadStart();
extern void go_threadEnd();
extern void go_tcpConnectRequest(ENDPOINT_ID id, PNF_TCP_CONN_INFO pConnInfo);
extern void go_tcpConnected(ENDPOINT_ID id, PNF_TCP_CONN_INFO pConnInfo);
extern void go_tcpClosed(ENDPOINT_ID id, PNF_TCP_CONN_INFO pConnInfo);
extern void go_tcpReceive(ENDPOINT_ID id, const char * buf, int len);
extern void go_tcpSend(ENDPOINT_ID id, const char * buf, int len);
extern void go_tcpCanReceive(ENDPOINT_ID id);
extern void go_tcpCanSend(ENDPOINT_ID id);
extern void go_udpCreated(ENDPOINT_ID id, PNF_UDP_CONN_INFO pConnInfo);
extern void go_udpConnectRequest(ENDPOINT_ID id, PNF_UDP_CONN_REQUEST pConnReq);
extern void go_udpClosed(ENDPOINT_ID id, PNF_UDP_CONN_INFO pConnInfo);
extern void go_udpReceive(ENDPOINT_ID id, const unsigned char * remoteAddress, const char * buf, int len, PNF_UDP_OPTIONS options);
extern void go_udpSend(ENDPOINT_ID id, const unsigned char * remoteAddress, const char * buf, int len, PNF_UDP_OPTIONS options);
extern void go_udpCanReceive(ENDPOINT_ID id);
extern void go_udpCanSend(ENDPOINT_ID id);

// forward to Golang callbacks.
void threadStart(){
	go_threadStart();
}
void threadEnd(){
	go_threadEnd();
}
void tcpConnectRequest(ENDPOINT_ID id, PNF_TCP_CONN_INFO pConnInfo){
	go_tcpConnectRequest(id,pConnInfo);
}
void tcpConnected(ENDPOINT_ID id, PNF_TCP_CONN_INFO pConnInfo){
	go_tcpConnected(id,pConnInfo);
}
void tcpClosed(ENDPOINT_ID id, PNF_TCP_CONN_INFO pConnInfo){
	go_tcpClosed(id,pConnInfo);
}
void tcpReceive(ENDPOINT_ID id, const char * buf, int len){
	go_tcpReceive(id,buf,len);
}
void tcpSend(ENDPOINT_ID id, const char * buf, int len){
	go_tcpSend(id,buf,len);
}
void tcpCanReceive(ENDPOINT_ID id){
	go_tcpCanReceive(id);
}
void tcpCanSend(ENDPOINT_ID id){
	go_tcpCanSend(id);
}
void udpCreated(ENDPOINT_ID id, PNF_UDP_CONN_INFO pConnInfo){
	go_udpCreated(id,pConnInfo);
}
void udpConnectRequest(ENDPOINT_ID id, PNF_UDP_CONN_REQUEST pConnReq){
	go_udpConnectRequest(id,pConnReq);
}
void udpClosed(ENDPOINT_ID id, PNF_UDP_CONN_INFO pConnInfo){
	go_udpClosed(id,pConnInfo);
}
void udpReceive(ENDPOINT_ID id, const unsigned char * remoteAddress, const char * buf, int len, PNF_UDP_OPTIONS options){
	go_udpReceive(id,remoteAddress,buf,len,options);
}
void udpSend(ENDPOINT_ID id, const unsigned char * remoteAddress, const char * buf, int len, PNF_UDP_OPTIONS options){
    go_udpSend(id,remoteAddress,buf,len,options);
}
void udpCanReceive(ENDPOINT_ID id){
	go_udpCanReceive(id);
}
void udpCanSend(ENDPOINT_ID id){
	go_udpCanSend(id);
}

NF_EventHandler eh = {
    threadStart,
    threadEnd,
    tcpConnectRequest,
    tcpConnected,
    tcpClosed,
    tcpReceive,
    tcpSend,
    tcpCanReceive,
    tcpCanSend,
    udpCreated,
    udpConnectRequest,
    udpClosed,
    udpReceive,
    udpSend,
    udpCanReceive,
    udpCanSend
};

NF_STATUS filter_tcp_connect_requests() {
	NF_RULE rule;
	memset(&rule, 0, sizeof(rule));
	rule.ip_family = AF_INET;
	rule.protocol = IPPROTO_TCP;
	rule.direction = NF_D_OUT;
	rule.filteringFlag = NF_INDICATE_CONNECT_REQUESTS;
	NF_STATUS status = nf_addRule(&rule, FALSE);
    if(status != NF_STATUS_SUCCESS) {
        return status;
    }
	memset(&rule, 0, sizeof(rule));
	rule.ip_family = AF_INET6;
	rule.protocol = IPPROTO_TCP;
	rule.direction = NF_D_OUT;
	rule.filteringFlag = NF_INDICATE_CONNECT_REQUESTS;
	status = nf_addRule(&rule, FALSE);
	if(status != NF_STATUS_SUCCESS) {
        return status;
    }

	// ::ffff:127.0.0.1
	unsigned char remoteIpAddress[16] = {
		0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0x00, 0x00,
		0x00, 0x00, 0xFF, 0xFF,
		0x7F, 0x00, 0x00, 0x01
	};

	memset(&rule, 0, sizeof(rule));
	rule.filteringFlag = NF_ALLOW;
	rule.protocol = IPPROTO_TCP;
	rule.ip_family = AF_INET6;
	memcpy(rule.remoteIpAddress, remoteIpAddress, sizeof(remoteIpAddress));
	return nf_addRule(&rule, TRUE);
}

NF_STATUS filter_tcp() {
    NF_RULE rule;
	memset(&rule, 0, sizeof(rule));
	rule.protocol = IPPROTO_TCP;
	rule.filteringFlag = NF_FILTER;
	return nf_addRule(&rule, FALSE);
}

// With this rule, the udpConnectRequest event fired when the client call net.DialUDP, the localAddress is 0.0.0.0:0 looks useless,
// It's not fired when the client call net.ListenUDP first then send to peers later.
NF_STATUS filter_udp_connect_requests() {
	NF_RULE rule;
	memset(&rule, 0, sizeof(rule));
	rule.ip_family = AF_INET;
	rule.protocol = IPPROTO_UDP;
	rule.direction = NF_D_OUT;
	rule.filteringFlag = NF_INDICATE_CONNECT_REQUESTS;
	NF_STATUS status = nf_addRule(&rule, FALSE);
    if(status != NF_STATUS_SUCCESS) {
        return status;
    }
	memset(&rule, 0, sizeof(rule));
	rule.ip_family = AF_INET6;
	rule.protocol = IPPROTO_UDP;
	rule.direction = NF_D_OUT;
	rule.filteringFlag = NF_INDICATE_CONNECT_REQUESTS;
	return nf_addRule(&rule, FALSE);
}

NF_STATUS filter_udp() {
    NF_RULE rule;
	memset(&rule, 0, sizeof(rule));
	rule.ip_family = AF_INET;
	rule.protocol = IPPROTO_UDP;
	rule.filteringFlag = NF_FILTER;
	NF_STATUS status = nf_addRule(&rule, FALSE);
    if(status != NF_STATUS_SUCCESS) {
        return status;
    }
	memset(&rule, 0, sizeof(rule));
	rule.ip_family = AF_INET6;
	rule.protocol = IPPROTO_UDP;
	rule.filteringFlag = NF_FILTER;
	return nf_addRule(&rule, FALSE);
}

// filter ip packets
// netfilter callbacks implement with Golang.
extern void go_ipReceive(
    const char * buf,
	int len,
	PNF_IP_PACKET_OPTIONS options,
	unsigned long	compartmentId,
	unsigned long	interfaceIndex,
	unsigned long	subInterfaceIndex,
	unsigned long	flags);

extern void go_ipSend(
    const char * buf,
	int len,
	PNF_IP_PACKET_OPTIONS options,
	unsigned long	compartmentId,
	unsigned long	interfaceIndex,
	unsigned long	subInterfaceIndex,
	unsigned long	flags);

// forward to Golang callbacks.
void ipReceive(const char * buf, int len, PNF_IP_PACKET_OPTIONS options){
	if (options == NULL) {
		go_ipReceive(buf,len,options,0,0,0,0);
	} else {
		go_ipReceive(buf,len,options,options->compartmentId,options->interfaceIndex,options->subInterfaceIndex,options->flags);
	}
}

void ipSend(const char * buf, int len, PNF_IP_PACKET_OPTIONS options){
	if (options == NULL) {
		go_ipSend(buf,len,options,0,0,0,0);
	} else {
		go_ipSend(buf,len,options,options->compartmentId,options->interfaceIndex,options->subInterfaceIndex,options->flags);
	}
}

NF_IPEventHandler ip_eh = {
	ipReceive,
	ipSend
};

NF_STATUS nf_sendpkg(
	const char *    buf,
	int             len,
	unsigned short	ip_family,
	unsigned int    ipHeaderSize,
	unsigned long	compartmentId,
	unsigned long	interfaceIndex,
	unsigned long	subInterfaceIndex,
	unsigned long	flags) {

    // 分配 NF_IP_PACKET_OPTIONS 对象的内存
    NF_IP_PACKET_OPTIONS *options = (NF_IP_PACKET_OPTIONS *)malloc(sizeof(NF_IP_PACKET_OPTIONS));
    if (options == NULL) {
        fprintf(stderr, "Failed to allocate memory for NF_IP_PACKET_OPTIONS\n");
        return 1;
    }

	options->ip_family = AF_INET;
	options->ipHeaderSize = 20;
	options->compartmentId = compartmentId;
	options->interfaceIndex = interfaceIndex;
	options->subInterfaceIndex = subInterfaceIndex;
	options->flags = flags;

	NF_STATUS res;
	res = nf_ipPostSend(buf, len, options);
	free(options);
	return res;
}

NF_STATUS nf_recvpkg(
	const char *    buf,
	int             len,
	unsigned short	ip_family,
	unsigned int    ipHeaderSize,
	unsigned long	compartmentId,
	unsigned long	interfaceIndex,
	unsigned long	subInterfaceIndex,
	unsigned long	flags) {

    // 分配 NF_IP_PACKET_OPTIONS 对象的内存
    NF_IP_PACKET_OPTIONS *options = (NF_IP_PACKET_OPTIONS *)malloc(sizeof(NF_IP_PACKET_OPTIONS));
    if (options == NULL) {
        fprintf(stderr, "Failed to allocate memory for NF_IP_PACKET_OPTIONS\n");
        return 1;
    }

	options->ip_family = AF_INET;
	options->ipHeaderSize = 20;
	options->compartmentId = compartmentId;
	options->interfaceIndex = interfaceIndex;
	options->subInterfaceIndex = subInterfaceIndex;
	options->flags = flags;

	NF_STATUS res;
	res = nf_ipPostReceive(buf, len, options);
	free(options);
	return res;
}

NF_STATUS filter_user_defined(unsigned long ipv4Addr, unsigned short remotePort, int protocol) {
	NF_RULE ruleIn;
	memset(&ruleIn, 0, sizeof(NF_RULE));
	ruleIn.ip_family = AF_INET;
	ruleIn.direction = NF_D_IN;
	ruleIn.protocol = protocol;
	ruleIn.remotePort = htons(remotePort);
	ruleIn.filteringFlag = NF_FILTER_AS_IP_PACKETS;
	*((unsigned long*)ruleIn.remoteIpAddress) = ipv4Addr;
	*((unsigned long*)ruleIn.remoteIpAddressMask) = inet_addr("255.255.255.255");
	return nf_addRule(&ruleIn, TRUE);
}

NF_STATUS filter_icmp() {
    NF_RULE rule;
	memset(&rule, 0, sizeof(rule));
	rule.ip_family = AF_INET;
	rule.protocol = IPPROTO_ICMP;
	rule.filteringFlag = NF_FILTER_AS_IP_PACKETS;
	NF_STATUS status = nf_addRule(&rule, FALSE);
    if(status != NF_STATUS_SUCCESS) {
        return status;
    }
	memset(&rule, 0, sizeof(rule));
	rule.ip_family = AF_INET6;
	rule.protocol = IPPROTO_ICMP;
	rule.filteringFlag = NF_FILTER_AS_IP_PACKETS;
	return nf_addRule(&rule, FALSE);
}

NF_STATUS filter_ip() {
    NF_RULE rule;
	memset(&rule, 0, sizeof(rule));
	rule.ip_family = AF_INET;
	rule.filteringFlag = NF_FILTER_AS_IP_PACKETS;
	NF_STATUS status = nf_addRule(&rule, FALSE);
    if(status != NF_STATUS_SUCCESS) {
        return status;
    }
	memset(&rule, 0, sizeof(rule));
	rule.ip_family = AF_INET6;
	rule.filteringFlag = NF_FILTER_AS_IP_PACKETS;
	return nf_addRule(&rule, FALSE);
}

void modify_addr4(void *p, const char* ip, uint16_t port) {
    struct sockaddr_in addr;
	memset(&addr, 0, sizeof(addr));
	addr.sin_family = AF_INET;
	// addr.sin_addr.s_addr = inet_addr(ip);
	inet_pton(AF_INET, ip, &addr.sin_addr);
	addr.sin_port = htons(port);
    memcpy(p, &addr, sizeof(addr));
}

void modify_addr6(void *p, const char* ip, uint16_t port) {
    struct sockaddr_in6 addr;
	memset(&addr, 0, sizeof(addr));
	addr.sin6_family = AF_INET6;
	inet_pton(AF_INET6, ip, &addr.sin6_addr);
	addr.sin6_port = htons(port);
    memcpy(p, &addr, sizeof(addr));
}

char* sockaddr_in6_to_string(struct sockaddr_in6 *addr) {
    char ip[INET6_ADDRSTRLEN];
    inet_ntop(AF_INET6, &(addr->sin6_addr), ip, INET6_ADDRSTRLEN);

    char *result = (char *)malloc(INET6_ADDRSTRLEN + 10); // 10 for port and extra characters
    if (result == NULL) {
        return NULL; // Allocation failed
    }

    snprintf(result, INET6_ADDRSTRLEN + 10, "[%s]:%d", ip, ntohs(addr->sin6_port));
    return result;
}

PNF_UDP_OPTIONS copyNF_UDP_OPTIONS(PNF_UDP_OPTIONS options) {
	PNF_UDP_OPTIONS p = (PNF_UDP_OPTIONS)malloc(sizeof(NF_UDP_OPTIONS)+options->optionsLength-1);
	memcpy(p, options, sizeof(NF_UDP_OPTIONS)-1+options->optionsLength);
	return p;
}

PNF_IP_PACKET_OPTIONS copyPNF_IP_PACKET_OPTIONS(PNF_IP_PACKET_OPTIONS options) {
	PNF_IP_PACKET_OPTIONS p = (PNF_IP_PACKET_OPTIONS)malloc(sizeof(NF_IP_PACKET_OPTIONS));
	memcpy(p, options, sizeof(NF_IP_PACKET_OPTIONS));
	return p;
}

BOOL disableWow64FsRedirection(PVOID *oldValue) {
	if(!Wow64DisableWow64FsRedirection(oldValue)) {
		return FALSE;
	}
}

void revertWow64FsRedirection(PVOID *oldValue) {
	 Wow64RevertWow64FsRedirection(*oldValue);
}

BOOL installDriver(LPCWSTR sourceFile, LPCWSTR destinationFile, BOOL wow64) {
    // System32 directory was redirect to SysWOW64 by default, turn off it when install.
    PVOID oldValue = NULL;
    BOOL ok = TRUE;
    struct _stat sourceStat;
    _wstat(sourceFile, &sourceStat);
    struct _stat destinationStat;
    _wstat(destinationFile, &destinationStat);
    DWORD errorCode = 0;
    if(sourceStat.st_size != destinationStat.st_size) {
      ok = CopyFileW(sourceFile, destinationFile, FALSE);
      errorCode = GetLastError();
    }
    if(errorCode != 0) {
      SetLastError(errorCode);
    }
    return ok;
}
*/
import "C"

var (
	anyAddr4             = netip.AddrFrom4([4]byte{0, 0, 0, 0})
	netfilterInitialized bool
	netfilterOpened      bool

	wow64RedirectValue uintptr
	initDriverMu       sync.Mutex = sync.Mutex{}

	processes = sync.Map{}

	NetFilterDriverName = "xlaccwfp"
)

type Verdict C.uint

const (
	NF_DROP   Verdict = 0
	NF_ACCEPT Verdict = 1
	NF_STOLEN Verdict = 2
	NF_QUEUE  Verdict = 3
	NF_REPEAT Verdict = 4
	NF_STOP   Verdict = 5
)

func ModifyAddr4(p unsafe.Pointer, addr string, port uint16) {
	caddr := C.CString(addr)
	defer C.free(unsafe.Pointer(caddr))
	C.modify_addr4(p, caddr, C.uint16_t(port))
}

// copyNF_UDP_OPTIONS copy options from netfilter, the caller MUST free it with C.free.
func copyNF_UDP_OPTIONS(options C.PNF_UDP_OPTIONS) C.PNF_UDP_OPTIONS {
	return C.copyNF_UDP_OPTIONS(options)
}

// Init netfilter.
func Init(name string) (err error) {
	if netfilterInitialized {
		return nil
	}
	initDriverMu.Lock()
	defer initDriverMu.Unlock()

	// register/init first
	registerErr := registerDriver(name)
	initErr := initDriver(name)
	if initErr == nil {
		return nil
	}

	registerErr = registerDriver(name)
	if registerErr != nil {
		return fmt.Errorf("nf_init register error: %s", registerErr.Error())
	}
	initErr = initDriver(name)
	if initErr != nil {
		return fmt.Errorf("nf_init init error: %s", initErr.Error())
	}
	fastlogger.Infof("nf_init driver '%s' initialized", name)
	netfilterInitialized = true
	return nil
}

// registerDriver
// Register driver, the driver file must put into system drivers directory first.
//
// NOTE: nf_registerDriverEx support register file without put it into drivers system directory, but it fails if file path contains chinese characters,
// convert driver file path to wide char string SHOULD fix this problem. Copy driver file into system32 directory bypass this problem completely.
func registerDriver(name string) (err error) {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))
	// Registers and starts a driver with specified name (without ".sys" extension).
	status := C.nf_registerDriver(cname)
	if status != C.NF_STATUS_SUCCESS {
		return fmt.Errorf("register netfilter driver '%s' failed: %v. errno=%v", name, int(status), C.GetLastError())
	}
	return nil
}

func initDriver(name string) error {
	cname := C.CString(name)
	defer C.free(unsafe.Pointer(cname))
	// Call this function to initialize an attachment with the hooking driver and set the event handler object.
	// The library calls NF_EventHandler methods from a separate thread.
	status := C.nf_init(cname, &C.eh)
	// The function returns NF_STATUS_FAIL in case if some other process is already attached to hooking driver.
	// The driver doesn't allow multiple attachments.
	// If additional process must filter the network data on the same system,
	// it is possible to rename and register another copy of the driver, then use the new name in nf_init call from another process.
	// NF_STATUS
	// 	NF_STATUS_SUCCESS		= 0,
	//	NF_STATUS_FAIL			= -1,
	//	NF_STATUS_INVALID_ENDPOINT_ID	= -2,
	//	NF_STATUS_NOT_INITIALIZED	= -3,
	//	NF_STATUS_IO_ERROR		= -4,
	//	NF_STATUS_REBOOT_REQUIRED	= -5
	if status != C.NF_STATUS_SUCCESS {
		return fmt.Errorf("init netfilter '%v' failed: %v. errno=%v", name, int(status), C.GetLastError())
	}

	// filter ip packets
	C.nf_setIPEventHandler(&C.ip_eh)
	return nil
}

func startFilter() error {
	if status := C.filter_ip(); status != C.NF_STATUS_SUCCESS {
		fastlogger.Errorf("filter tcp failed: %v", status)
		return fmt.Errorf("add netfilter rule failed: %v", status)
	}
	// if status := C.filter_tcp_connect_requests(); status != C.NF_STATUS_SUCCESS {
	// 	fastlogger.Errorf("filter tcp connect requests failed: %v", status)
	// 	return fmt.Errorf("add netfilter rule failed: %v", status)
	// }
	fastlogger.Infof("filter tcp connect requests")

	// if false {
	// 	if status := C.filter_udp_connect_requests(); status != C.NF_STATUS_SUCCESS {
	// 		fastlogger.Errorf("filter udp connect requests failed: %v", status)
	// 		return fmt.Errorf("add netfilter rule failed: %v", status)
	// 	}
	// 	fastlogger.Infof("filter udp connect requests")
	// }

	// if status := C.filter_udp(); status != C.NF_STATUS_SUCCESS {
	// 	fastlogger.Errorf("filter udp failed: %v", status)
	// 	return fmt.Errorf("add netfilter rule failed: %v", status)
	// }
	// fastlogger.Infof("filter udp")
	return nil
}

func NetfilterSendIpPkg(packetData []byte, options NfIpPacketOptions) error {
	cData := (*C.char)(unsafe.Pointer(&packetData[0]))
	cLen := C.int(len(packetData))
	cIphdrSize := C.uint(options.IpHeaderSize)
	res := C.nf_sendpkg(cData, cLen, C.ushort(options.IpFamily), cIphdrSize, C.ulong(options.CompartmentId), C.ulong(options.InterfaceIndex), C.ulong(options.SubInterfaceIndex), C.ulong(options.Flags))
	if res != C.NF_STATUS_SUCCESS {
		return fmt.Errorf("send pkg failed: %v", res)
	}
	return nil
}

func NetfilterRecvIpPkg(packetData []byte, options NfIpPacketOptions) error {
	cData := (*C.char)(unsafe.Pointer(&packetData[0]))
	cLen := C.int(len(packetData))
	cIphdrSize := C.uint(options.IpHeaderSize)
	res := C.nf_recvpkg(cData, cLen, C.ushort(options.IpFamily), cIphdrSize, C.ulong(options.CompartmentId), C.ulong(options.InterfaceIndex), C.ulong(options.SubInterfaceIndex), C.ulong(options.Flags))
	if res != C.NF_STATUS_SUCCESS {
		return fmt.Errorf("send pkg failed: %v", res)
	}
	return nil
}

// func deleteDriverFile(name string) error {
// 	disableWow64FsRedirection()
// 	defer revertWow64FsRedirection()

// 	destinationDir := "C:\\Windows\\System32\\drivers"
// 	if path, err := windows.GetSystemDirectory(); err == nil {
// 		destinationDir = filepath.Join(path, "drivers")
// 	}
// 	destinationFile := filepath.Join(destinationDir, name+".sys")

// 	err := os.Remove(destinationFile)
// 	if err != nil {
// 		return err
// 	}
// 	return nil
// }

// refreshNetfilterEnv refresh all the nf env
// func RefreshNetfilterEnv(name string) error {
// 	finalErr := errors.New("")
// 	// only clean nf driver if Init failed
// 	// Stops the filtering thread, breaks all filtered connections and detaches from the hooking driver.
// 	C.nf_free()
// 	// stop/delete driver
// 	answer, err := sys.ScStop(name)
// 	if err != nil {
// 		finalErr = errors.Wrapf(finalErr, "stop nf driver '%v', output: %s", name, answer)
// 	}
// 	raw, err := sys.ScDelete(name)
// 	if err != nil {
// 		finalErr = errors.Wrapf(finalErr, "delete nf driver '%v', output: %s", name, raw)
// 	}

// 	// delete file
// 	err = deleteDriverFile(name)
// 	if err != nil {
// 		finalErr = errors.Wrapf(finalErr, "delete nf file failed '%v'", name)
// 	}
// 	if finalErr.Error() == "" {
// 		return nil
// 	}
// 	return finalErr
// }

// disableWow64FsRedirection
//
// must defer dial revertWow64FsRedirection
// func disableWow64FsRedirection() {
// 	C.disableWow64FsRedirection((*C.PVOID)(unsafe.Pointer(&wow64RedirectValue)))
// }

// func revertWow64FsRedirection() {
// 	C.revertWow64FsRedirection((*C.PVOID)(unsafe.Pointer(&wow64RedirectValue)))
// }

// Open netfilter.
func Open() (err error) {
	if netfilterOpened {
		return nil
	}

	ll := fastlogger.WithField("report", true)
	begin := time.Now()
	defer func() {
		if err == nil {
			netfilterOpened = true
			ll.Infof("open netfilter takes %v", time.Now().Sub(begin).String())
		} else {
			ll.WithError(err).Errorf("open netfilter failed")
		}
	}()

	var initErr error
	for i := 0; i < 2; i++ {
		initErr = Init(NetFilterDriverName)
		if initErr == nil {
			break
		}
	}
	if initErr != nil {
		return initErr
	}

	if err := startFilter(); err != nil {
		return err
	}

	// init sessions
	// DefaultSesions = NewTCPSessions()
	return nil
}

// Close netfilter.
func Close() {
	// UpdateSetting(config.NetfilterSettings{})
	begin := time.Now()
	C.nf_deleteRules()
	//C.nf_free()
	fastlogger.WithField("report", true).Infof("close netfilter takes %v", time.Now().Sub(begin).String())
	netfilterOpened = false
	//netfilterInitialized = false
}
